/*
 * Copyright 2011 Mark Davidson.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.dctmcommons.impl;

import java.util.ArrayList;
import java.util.List;

import org.dctmcommons.IWorkflowHelper;
import org.dctmcommons.MoveToNextActivityException;
import org.dctmcommons.WorkflowTemplateNotInstalledException;

import com.documentum.fc.client.IDfActivity;
import com.documentum.fc.client.IDfCollection;
import com.documentum.fc.client.IDfPackage;
import com.documentum.fc.client.IDfProcess;
import com.documentum.fc.client.IDfSession;
import com.documentum.fc.client.IDfWorkflow;
import com.documentum.fc.client.IDfWorkflowBuilder;
import com.documentum.fc.client.IDfWorkitem;
import com.documentum.fc.common.DfDocbaseConstants;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.DfId;
import com.documentum.fc.common.DfList;
import com.documentum.fc.common.DfLogger;
import com.documentum.fc.common.IDfId;
import com.documentum.fc.common.IDfList;

import static org.dctmcommons.DctmCommonsConstants.*;

/**
 * Workflow related helper methods.
 * @author Mark Davidson
 */
public class WorkflowHelper implements IWorkflowHelper {

	/** Error displayed on attempting to instantiate an uninstalled workflow. */
	private static final String MSG_UNINSTALLED_WORKFLOW_TEMPLATE_RUN_ERROR =
		"Workflow template cannot be instantiated as it is not installed: ";

	/** Error displayed on moving to an invalid activity index. */
	private static final String MSG_MOVE_TO_INVALID_ACTIVITY_INDEX =
		"Attempted to move to an invalid activity index";

	/** Token for the process name. */
	private final static String TOKEN_PROCESS_NAME = "PROCESS_NAME";
	
	/** DQL template for getting a workflow template by object name. */
	private final static String DQL_GET_PROCESS_TEMPLATE_BY_NAME =
		"dm_process where object_name = '" + TOKEN_PROCESS_NAME + "'";
	
	/** Token for the qualification ending. */
	private final static String TOKEN_QUAL_END = "QUAL_END";
	
	/** DQL template for getting a workflow template by qualification. */
	private final static String DQL_GET_PROCESS_TEMPLATE_BY_QUAL =
		"dm_process where " + TOKEN_QUAL_END;
	
	/**
	 * Default constructor.
	 */
	private WorkflowHelper() {}
	
	/**
	 * Factory method.
	 * @return A new workflow helper instance
	 */
	public static IWorkflowHelper getInstance() {
		return new WorkflowHelper();
	}
	
	/**
	 * Launches a workflow.
	 * @param session The session
	 * @param workflowName The name of the process template
	 * @param documentId The object id of the package document
	 * @throws DfException The repository exception
	 */
	public IDfWorkflow startWorkflow(final IDfSession session,
									 final String workflowName,
									 final IDfId documentId)
		throws DfException {
		
		assert session != null : "Mandatory parameter session is null";
		assert workflowName != null :
									"Mandatory parameter workflowName is null";
		
		final IDfProcess workflowTemplate = getWorkflowTemplateByName(
															session,
															workflowName);
		
		if (workflowTemplate.getInt(R_DEFINITION_STATE) != WF_STATE_INSTALLED) {
			throw new WorkflowTemplateNotInstalledException(
					MSG_UNINSTALLED_WORKFLOW_TEMPLATE_RUN_ERROR + workflowName);
		}
		
		final IDfWorkflowBuilder wfBuilder = session.newWorkflowBuilder(
												workflowTemplate.getObjectId());
		wfBuilder.initWorkflow();
		wfBuilder.runWorkflow();
		
		// get the workflow instance
		final IDfWorkflow workflow = wfBuilder.getWorkflow();
		
		// add package if required
		if (documentId != null) {
			final String firstActivityName = wfBuilder.getStartActivityNames()
																.getString(0);
			final IDfId firstActivityId = (IDfId) wfBuilder
												.getStartActivityIds().get(0);
			final IDfActivity firstActivity = (IDfActivity) session.getObject(
															firstActivityId);
			
			String inputPortName = null;
			String inputPackageName = null;
			
			// get input port details
			for (int i = 0; i < firstActivity.getPortCount(); i++) {
				if (firstActivity.getPortType(i).equals(WF_PORT_TYPE_INPUT)) {
					inputPortName = firstActivity.getPortName(i);
					inputPackageName = firstActivity.getPackageName(i);
				}
			}
			
			final IDfList list = new DfList();
			list.append(documentId);
			
			final String typeName = session.getObject(documentId).getType()
																	.getName();
			final IDfId packageId = wfBuilder.addPackage(firstActivityName,
														 inputPortName,
														 inputPackageName,
														 typeName,
														 null,
														 false,
														 list);
			DfLogger.debug(this, "Added package: " + packageId, null, null);
		}
		
		DfLogger.debug(this,
					   "Launched workflow: " + workflow.getObjectId(),
					   null,
					   null);
		
		return workflow;
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.dctmcommons.IWorkflowHelper#getProcessByName(com.documentum.fc.client.IDfSession, java.lang.String)
	 */
	@Override
	public IDfProcess getWorkflowTemplateByName(final IDfSession session,
												final String processName)
		throws DfException {
		
		assert session != null : "Mandatory parameter session is null";
		assert processName != null : "Mandatory parameter processName is null";
		
		final String dql = DQL_GET_PROCESS_TEMPLATE_BY_NAME.replaceFirst(
															TOKEN_PROCESS_NAME,
															processName);
		return (IDfProcess) session.getObjectByQualification(dql);
	}

	/*
	 * (non-Javadoc)
	 * @see org.dctmcommons.IWorkflowHelper#getProcessByQualification(com.documentum.fc.client.IDfSession, java.lang.String)
	 */
	@Override
	public IDfProcess getWorkflowTemplateByQualification(
													final IDfSession session,
													final String qualification)
		throws DfException {
		
		final String dql = DQL_GET_PROCESS_TEMPLATE_BY_QUAL.replaceFirst(
																TOKEN_QUAL_END,
																qualification);
		return (IDfProcess) session.getObjectByQualification(dql);
	}

	/*
	 * (non-Javadoc)
	 * @see org.dctmcommons.IWorkflowHelper#setNextActivity(com.documentum.fc.client.IDfWorkitem, int, boolean)
	 */
	@Override
	public void moveToNextActivity(final IDfWorkitem workitem,
								final int activityIndex,
								final boolean isForwardActivity)
		throws DfException {
		
		assert workitem != null : "Mandatory parameter workitem is null";
		
		final IDfList activities = isForwardActivity ?
								       workitem.getForwardActivities() :
								    	   workitem.getRejectActivities();

		if (activities.getCount() < activityIndex) {
			throw new MoveToNextActivityException(
											MSG_MOVE_TO_INVALID_ACTIVITY_INDEX);
		}
		
		final IDfActivity activity = (IDfActivity) activities
														.get(activityIndex);
		final IDfList nextActivities = new DfList();
		nextActivities.append(activity);
		workitem.setOutputByActivities(nextActivities);
	}

	/*
	 * (non-Javadoc)
	 * @see org.dctmcommons.IWorkflowHelper#moveToDefaultForwardActivity(com.documentum.fc.client.IDfWorkitem)
	 */
	@Override
	public void moveToDefaultForwardActivity(final IDfWorkitem workitem)
			throws DfException {
		
		moveToNextActivity(workitem, 0, true);
	}

	/*
	 * (non-Javadoc)
	 * @see org.dctmcommons.IWorkflowHelper#moveToDefaultRejectActivity(com.documentum.fc.client.IDfWorkitem)
	 */
	@Override
	public void moveToDefaultRejectActivity(final IDfWorkitem workitem)
			throws DfException {
		
		moveToNextActivity(workitem, 0, false);
	}

	/*
	 * (non-Javadoc)
	 * @see org.dctmcommons.IWorkflowHelper#getDefaultPackageObjectId(com.documentum.fc.client.IDfWorkitem)
	 */
	@Override
	public IDfId getDefaultPackageObjectId(final IDfWorkitem workitem)
			throws DfException {
		
		assert workitem != null : "Mandatory parameter workitem is null";

		final String[][] packageObjIds = getPackageObjectIds(workitem);
		
		if (packageObjIds == null) {
			return null;
		} else {
			final String packageObjId = packageObjIds[0][0];
			return new DfId(packageObjId);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.dctmcommons.IWorkflowHelper#getPackageObjectIds(com.documentum.fc.client.IDfWorkitem)
	 */
	@Override
	public String[][] getPackageObjectIds(final IDfWorkitem workitem)
			throws DfException {
		
		assert workitem != null : "Mandatory parameter workitem is null";
		
		final List<String[]> packageDocIds = new ArrayList<String[]>();
		
		if (workitem != null) {
			final IDfCollection packages = workitem.getPackages("");
			
			if (packages != null) {
				try {
					while (packages.next()) {
						final int numDocs = packages
											.getValueCount("r_component_id");
						if (numDocs > 0) {
							final String[] docIds = new String[numDocs];
							for (int i = 0; i < numDocs; i++) {
								final String docId = packages
											.getRepeatingId("r_component_id", i)
												.getId();
								docIds[i] = docId;
							}
							packageDocIds.add(docIds);
						}
					}
				} catch (DfException e) {
					DfLogger.error(
								this,
								"Failed to iterate workflow package documents",
								null,
								e);
					throw e;
				} finally {
					packages.close();
				}
			}
		}
		
		if (packageDocIds.size() == 0) {
			return null;
		} else {
			return packageDocIds.toArray(new String[][] {});
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.dctmcommons.IWorkflowHelper#addNoteToDefaultPackage(com.documentum.fc.client.IDfWorkitem, java.lang.String)
	 */
	@Override
	public void addNoteToDefaultPackage(final IDfWorkitem workitem,
										final String text)
			throws DfException {
		
		assert workitem != null : "Mandatory parameter workitem is null";
		assert text != null : "Mandatory parameter text is null";
		
		final IDfSession session = workitem.getSession();
		final IDfCollection packages = workitem.getPackages("");
		
		if (packages != null && packages.next()) {
			try { 
				final IDfId packageId = packages.getId(
												DfDocbaseConstants.R_OBJECT_ID);
				final IDfPackage firstPackage = (IDfPackage) session.getObject(
																	packageId);
				
				if (firstPackage != null) {
					firstPackage.appendNote(text, false);
				}
			} finally {
				packages.close();
			}
		}
	}
}
